﻿using static System.Math;

namespace GeoFits
{
    /// <summary>
    /// 几何图形类：向量
    /// </summary>
    public class Vector : Geometry
    {
        #region 构造函数
        /// <summary>
        /// 构造一个向量
        /// </summary>
        /// <param name="x">X分量</param>
        /// <param name="y">Y分量</param>
        /// <param name="z">Z分量</param>
        public Vector(double x, double y, double z)
        { X = x; Y = y; Z = z; }
        /// <summary>
        /// 构造一个0向量
        /// </summary>
        public Vector()
        { X = 0; Y = 0; Z = 0; }
        #endregion
        #region 属性
        /// <summary>
        /// 向量的X分量
        /// </summary>
        /// <value>向量的X分量</value>
        public double X { get; set; }
        /// <summary>
        /// 向量的Y分量
        /// </summary>
        /// <value>向量的Y分量</value>
        public double Y { get; set; }
        /// <summary>
        /// 向量的Z分量
        /// </summary>
        /// <value>向量的Z分量</value>
        public double Z { get; set; }
        /// <summary>
        /// 向量长度
        /// </summary>
        /// <value>向量长度</value>
        public double Length
        {
            get
            {
                return Sqrt(X * X + Y * Y + Z * Z);
            }
        }
        #endregion

        /// <summary>
        /// 两向量的点乘（数量积）
        /// </summary>
        /// <param name="v2">第二个向量</param>
        /// <returns>点乘的结果</returns>
        public double DotProduct(Vector v2)
        {
            return X * v2.X + Y * v2.Y + Z * v2.Z;
        }
        /// <summary>
        /// 两向量的向量积（叉乘）
        /// </summary>
        /// <param name="des">第二个向量</param>
        /// <returns>向量积结果</returns>
        public Vector CrossProduct(Vector des)
        {
            return new Vector(Y * des.Z - Z * des.Y, Z * des.X - X * des.Z, X * des.Y - Y * des.X);
        }
        /// <summary>
        /// 获取一个与当前向量同向的单位向量
        /// </summary>
        /// <returns>与向量方向相同的方向向量</returns>
        /// <exception cref="System.Exception">如果向量为0向量，抛出Exception，无法化为单位向量。</exception>
        public Vector Normalize()
        {
            double len = Length;
            if (len == 0.0)
            {
                return new Vector(this.X, this.Y, this.Z);
            }
            return new Vector(X / len, Y / len, Z / len);
        }
        /// <summary>
        /// 求出两个向量的夹角
        /// </summary>
        /// <param name="v2">另一个向量</param>
        /// <returns>两向量的夹角</returns>
        /// <remarks>
        /// <para>返回值的单位为弧度</para>
        /// </remarks>
        public double getAngle(Vector v2)
        {
            if (Length == 0 || v2.Length == 0)
            {
                throw new VectorZeroLengthException();
            }
            double angle = Acos(DotProduct(v2) / (Length * v2.Length));
            return angle;
        }
        ///// <summary>
        ///// 判定方法总述
        ///// 1、两个向量求向量积的时候from，des和result总会构成一个右手系，所以如果result的Z>0则角小于180，如果result的Z小于0角大于180
        ///// 2、当Z=0时将坐标依次替换，构成另外一个右手系（X=>Y, Y=>Z, Z=>X）
        ///// </summary>
        ///// <param name="from">起始向量</param>
        ///// <param name="des">最终向量</param>
        ///// <returns>是否为大于180度的角</returns>
        //private static bool AngleBiggerThanPI(Vector from, Vector des)
        //{
        //    //Vector cross = CrossProduct(from, des);
        //    //Quaternion qt = Quaternion.getQuaternion(cross, new Vector(0, 0, 1));
        //    //Vector formAftRt = from * qt;
        //    //Vector desAftRt = des * qt;
        //    double crossZ = CrossProduct(from, des).Z;
        //    if (crossZ > 0)
        //    {
        //        return false;
        //    }
        //    else if (crossZ < 0)
        //    {
        //        return true;
        //    }
        //    else
        //    {
        //        return AngleBiggerThanPI(new Vector(from.Z, from.X, from.Y), new Vector(des.Z, des.X, des.Y));
        //    }
        //}
        #region 静态方法
        /// <summary>
        /// 静态方法：两向量的向量积（叉乘）
        /// </summary>
        /// <param name="from">第一个向量</param>
        /// <param name="des">第二个向量</param>
        /// <returns>向量积结果</returns>
        public static Vector CrossProduct(Vector from, Vector des)
        { return from.CrossProduct(des); }
        /// <summary>
        /// 两向量的点乘（数量积）
        /// </summary>
        /// <param name="v1">第一个向量</param>
        /// <param name="v2">第二个向量</param>
        /// <returns>点乘的结果</returns>
        public static double DotProduct(Vector v1, Vector v2)
        { return v1.DotProduct(v2); }
        /// <summary>
        /// 求出两个向量的夹角
        /// </summary>
        /// <param name="v1">第一个向量</param>
        /// <param name="v2">另一个向量</param>
        /// <returns>两向量的夹角</returns>
        /// <remarks>
        /// <para>返回值的单位为弧度</para>
        /// </remarks>
        public static double getAngle(Vector v1, Vector v2)
        { return v1.getAngle(v2); }
        #endregion

        /// <summary>
        /// 判断两个向量是是否相等
        /// </summary>
        /// <param name="v">第二个向量</param>
        /// <returns>相等返回true，不相等返回false</returns>
        public bool Equals(Vector v)
        {
            if (ReferenceEquals(v, null))
                return false;
            else
                return X == v.X && Y == v.Y && Z == v.Z;
        }
        /// <summary>
        /// 判断类和向量是是否相等
        /// </summary>
        /// <param name="obj">与向量相比较的类</param>
        /// <returns>相等返回true，不相等返回false</returns>
        public override bool Equals(object obj)
        {
            if (GetType() != obj.GetType())
                return false;
            else
            {
                Vector v = obj as Vector;
                return Equals(v);
            }
        }
        /// <summary>
        /// 产生Key
        /// </summary>
        /// <returns>Key</returns>
        public override int GetHashCode()
        { return base.GetHashCode(); }
        #region 运算符的重载
        /// <summary>
        /// 两向量相减
        /// </summary>
        /// <param name="v1">第一个向量</param>
        /// <param name="v2">第二个向量</param>
        /// <returns>两向量之差</returns>
        public static Vector operator -(Vector v1, Vector v2)
        { return new Vector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z); }
        /// <summary>
        /// 两向量是否相等
        /// </summary>
        /// <param name="v1">第一个向量</param>
        /// <param name="v2">第二个向量</param>
        /// <returns>相等则返回true，不相等返回false</returns>
        public static bool operator ==(Vector v1, Vector v2)
        {
            if (ReferenceEquals(v1, null) && ReferenceEquals(v2, null))
                return true;
            else if (ReferenceEquals(v1, null))
                return v2.Equals(v1);
            else
                return v1.Equals(v2);
        }
        /// <summary>
        /// 两向量是否不相等
        /// </summary>
        /// <param name="v1">第一个向量</param>
        /// <param name="v2">第二个向量</param>
        /// <returns>不相等则返回true，相等返回false</returns>
        public static bool operator !=(Vector v1, Vector v2)
        { return !(v1 == v2); }
        /// <summary>
        /// 数与向量的乘积
        /// </summary>
        /// <param name="real">实数</param>
        /// <param name="v1">向量</param>
        /// <returns>积</returns>
        public static Vector operator *(double real, Vector v1)
        { return new Vector(real * v1.X, real * v1.Y, real * v1.Z); }
        /// <summary>
        /// 向量与数的乘积
        /// </summary>
        /// <param name="v1">向量</param>
        /// <param name="real">实数</param>
        /// <returns>积</returns>
        public static Vector operator *(Vector v1, double real)
        { return real * v1; }
        /// <summary>
        /// 两向量的加法
        /// </summary>
        /// <param name="v1">第一个向量</param>
        /// <param name="v2">第二个向量</param>
        /// <returns>和</returns>
        public static Vector operator +(Vector v1, Vector v2)
        { return new Vector(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z); }
        #endregion
    }
}
